package com.sky.aspect;

import com.sky.annotation.AutoFill;
import com.sky.constant.AutoFillConstant;
import com.sky.context.BaseContext;
import com.sky.enumeration.OperationType;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.time.LocalDateTime;

/**
 * 表达自动填充的切面类
 */
@Component // 交给spring管理
@Aspect    //  是一个切面类
@Slf4j
public class AutoFillAspect {


    // 切面 =  切入点+通知= 切入点+方位+增强代码  对谁在哪做怎样的增强
    // 提取切入点表达式 用注解形式找切入点
//    @Pointcut("@annotation(com.sky.annotation.AutoFill)")
//    public void autoFillPointCut(){}

    @Before("@annotation(com.sky.annotation.AutoFill)")
    public void autoFill(JoinPoint joinPoint){//joinPoint 封装了原始方法的相关信息
        // jointPoint 被增强方法   categoryMapper.insert(category)  原始方法
        //                        categoryMapper.update(category);
        //  1:找到原始方法 判断是做新增还是更新
        log.info("完成自动填充 ");
        // 1.1 根据 joinPonit 找到原始方法的 反射形式
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        Method method = signature.getMethod();
        // method指的是原始方法--被增强的方法  categoryMapper.insert(category)  categoryMapper.update(category);
        // 1.2 要解析 方法上的注解  @AutoFill
        AutoFill af =  method.getAnnotation(AutoFill.class);
        // 获取af中的属性值
        OperationType ot = af.value(); //ot是 获取的操作字段 OperationType.UPDATE OperationType.INSERT

        // 2 获取 原始方法参数对象  再进行 自动填充
       //  根据 joinPonit 获取原始方法的相关信息  可以获取参数列表
        Object[] args = joinPoint.getArgs();
        // 参数 是在参数列表第一位  categoryMapper.insert(category)  employeeMapper.update(employee);
        Object entry = args[0]; // entry 代表参数列表的 对象 可能是Category Employee Dish。。。类型

        // 获取当前时间
        LocalDateTime now = LocalDateTime.now();
        Long currentId = BaseContext.getCurrentId();//获取当前登录人
        // 2.1 如果更新  对原始方法参数的两个字段 updateTime updateUser实现自动填充
        if(ot==OperationType.UPDATE){//是更新操作
           log.info("更新的自动填充");
           //  缺俩字段   updateTime updateUser
           // 需要补上  entry.setUpdateTime(当前时间)  entry.setUpdateUser(当前登录人)
           // 反射形式  调用 entry的 setUpdateTime方法
            // 获取字节码对象.getDeclaredMethod(方法名,参数类型) 得到指定方法
            //      通过指定方法invoke()
            try {
                Method setUpdateTimeMethod = entry.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                Method setUpdateUserMethod = entry.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);

                // 反射执行
                setUpdateTimeMethod.invoke(entry,now);
                setUpdateUserMethod.invoke(entry,currentId);

            } catch (Exception e) {
                System.out.println("没有对应的set方法 请检查是否用了lombok");
                e.printStackTrace();//打印异常
            }

        } else {//是新增操作
            // 2.2 如果是新增  对原始方法参数的四个字段 updateTime updateUser createTime createUser实现自动填充
           log.info("新增的自动填充");
            //  缺四字段   updateTime updateUser  createTime createUser
            // 需要补上  entry.setUpdateTime(当前时间)  entry.setUpdateUser(当前登录人) ...

            try {
                Method setUpdateTimeMethod = entry.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_TIME, LocalDateTime.class);
                Method setUpdateUserMethod = entry.getClass().getDeclaredMethod(AutoFillConstant.SET_UPDATE_USER, Long.class);
                Method setCreateTimeMethod = entry.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_TIME, LocalDateTime.class);
                Method setCreateUserMethod = entry.getClass().getDeclaredMethod(AutoFillConstant.SET_CREATE_USER, Long.class);

                // 反射执行
                setCreateTimeMethod.invoke(entry,now);
                setCreateUserMethod.invoke(entry,currentId);
                // 反射执行
                setUpdateTimeMethod.invoke(entry,now);
                setUpdateUserMethod.invoke(entry,currentId);

            } catch (Exception e) {
                System.out.println("没有对应的set方法 请检查是否用了lombok");
                e.printStackTrace();//打印异常
            }
        }


    }
}
